package org.vacoor.nothing.shop.entity;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.vacoor.nothing.common.util.Resources;
import org.vacoor.nothing.common.util.StringUtils;
import org.vacoor.nothing.shop.OrderedProperties;
import org.vacoor.nothing.web.Nls;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import static org.vacoor.nothing.common.util.Castor.asInt;
import static org.vacoor.nothing.common.util.Castor.asLong;

/**
 * Created by Administrator on 2015/2/4.
 */
public class M {
    static class Pair<L, R> {
        private L left;
        private R right;

        Pair(L left, R right) {
            this.left = left;
            this.right = right;
        }

        public L getLeft() {
            return left;
        }

        public R getRight() {
            return right;
        }
    }

    public static void main(String[] args) throws IOException, TemplateException {
        final String NLS_ANNO_FULL_NAME = Nls.class.getName();
        final String DESC_TAG = "desc";

        JavaDocBuilder builder = new JavaDocBuilder();
        builder.addSourceTree(new File("C:\\Users\\Administrator\\IntelliJProjects\\nothing\\nothing-shop\\src\\main\\java\\org\\vacoor\\nothing\\shop\\entity"));
        builder.addSourceTree(new File("E:\\gx\\WorkerInformation\\WorkerInformation\\src\\main\\java\\com\\WorkerInformation\\entity"));
//        builder.addSourceTree(new File("/Users/vacoor/IdeaProjects/nothing/nothing-shop/src/main/java/org/vacoor/nothing/shop/entity"));
        JavaClass[] classes = builder.getClasses();

        // classes = new JavaClass[]{builder.getClassByName("org.vacoor.nothing.shop.entity.Brand")};
        // classes = new JavaClass[]{builder.getClassByName("org.vacoor.nothing.shop.entity.Specification")};
        // classes = new JavaClass[]{builder.getClassByName("org.vacoor.nothing.shop.entity.Model")};
        classes = new JavaClass[]{builder.getClassByName("com.WorkerInformation.entity.product.DetailTemplate")};

        // 字段及 NLS 信息
        Map<String/*field*/, Pair<String, String>/*code,desc*/> nlsMap = Maps.newLinkedHashMap();
        Map<String, FormField> testMap = Maps.newLinkedHashMap();

        for (JavaClass aClass : classes) {
            JavaField[] fields = aClass.getFields();
            for (JavaField field : fields) {
                String name = field.getName();
                String type = field.getType().getJavaClass().getFullyQualifiedName();
                String fullName = field.getParentClass().getFullyQualifiedName() + "." + name;
                String code = null;

                // 提取 NLS 信息
                DocletTag descTag = field.getTagByName(DESC_TAG);
                String desc = null != descTag ? descTag.getValue() : fullName;
                desc = StringUtils.hasText(desc) ? desc : fullName;
                for (Annotation anno : field.getAnnotations()) {
                    if (NLS_ANNO_FULL_NAME.equals(anno.getType().getFullyQualifiedName())) {
                        code = (String) anno.getNamedParameter("value");
                        code = code.startsWith("\"") ? code.substring(1) : code;
                        code = code.endsWith("\"") ? code.substring(0, code.length() - 1) : code;

                        if (!StringUtils.hasText(code)) {
                            throw new IllegalStateException(field + " @Nls value is empty");
                        }

                        nlsMap.put(fullName, new Pair<String, String>(code, desc));
                        break;
                    }
                }
                if (null == code) {
                    nlsMap.put(fullName, new Pair<String, String>(code = fullName, desc));
                }

                // 提取字段类型等信息
                Rule rule = buildJqueryValidationRule(field);
                testMap.put(name, new FormField(name, fullName, code, type, rule));
            }
        }

        for (Map.Entry<String, FormField> entry : testMap.entrySet()) {
            FormField field = entry.getValue();
//            System.out.println(entry.getKey() + ":" + entry.getValue().getCode() + ":" + Jacksons.serialize(entry.getValue().getRule().getOptions()));
        }
        System.out.println();
        Configuration config = new Configuration();
//        config.setClassicCompatible(true);
//        config.setNumberFormat("number");
//        config.setSetting("boolean_format", "true,false");

        // Template tpl = new Template("xx", Resources.getResourceAsReader("jQueryValidation.ftl"), config);
        Template tpl = new Template("JqGrid", Resources.getResourceAsReader("/tpl4glanway/JqGrid.ftl"), config);
        tpl.process(ImmutableMap.of("formFields", testMap.values()), new PrintWriter(System.out));

        // save NLS message
        Properties props = new OrderedProperties();
        for (Pair<String, String> pair : nlsMap.values()) {
            props.setProperty(pair.getLeft(), pair.getRight());
        }
        props.store(new FileWriter("messages.properties"), "nls messages");
        props.list(System.out);
        System.out.println();
    }

    public static Rule buildJqueryValidationRule(AbstractBaseJavaEntity e) {
        Rule rule = Rule.create(e.getName());
        Annotation[] annos = e.getAnnotations();
        for (Annotation anno : annos) {
            String annoType = anno.getType().getFullyQualifiedName();
            if ("javax.validation.constraints.NotNull".equals(annoType)) {
                rule.addOption("required", true);
                continue;
            }
            if ("javax.validation.constraints.Size".equals(annoType)) {
                Object o = anno.getNamedParameter("min");
                int min = null != o ? asInt(o) : 0;
                o = anno.getNamedParameter("max");
                int max = null != o ? asInt(o) : 255;

                rule.addOption("minlength", min);
                rule.addOption("maxlength", max);
                continue;
            }
            if ("javax.validation.constraints.Min".equals(annoType)) {
                Object min = anno.getNamedParameter("value");

                rule.addOption("min", asLong(min));
                continue;
            }
            if ("javax.validation.constraints.Max".equals(annoType)) {
                Object max = anno.getNamedParameter("value");

                rule.addOption("max", asLong(max));
                continue;
            }
            if ("javax.validation.constraints.DecimalMin".equals(annoType)) {
                String min = (String) anno.getNamedParameter("value");
                min = min.startsWith("\"") ? min.substring(1) : min;
                min = min.endsWith("\"") ? min.substring(0, min.length() - 1) : min;

                rule.addOption("min", min);
                continue;
            }
            if ("javax.validation.constraints.DecimalMax".equals(annoType)) {
                String max = (String) anno.getNamedParameter("value");
                max = max.startsWith("\"") ? max.substring(1) : max;
                max = max.endsWith("\"") ? max.substring(0, max.length() - 1) : max;

                rule.addOption("max", max);
                continue;
            }
            if ("javax.validation.constraints.Pattern".equals(annoType)) {
                String regexp = (String) anno.getNamedParameter("regexp");
                // regexp = regexp.startsWith("\"") ? regexp.substring(1) : regexp;
                // regexp = regexp.endsWith("\"") ? regexp.substring(0, regexp.length() - 1) : regexp;
                // regexp = regexp.replace("\\\\", "\\");

                rule.addOption("pattern", regexp);
                continue;
            }
            if ("javax.validation.constraints.Digits".equals(annoType)) {
                int fraction = asInt(anno.getNamedParameter("fraction"));
                if (0 == fraction) {
                    rule.addOption("digits", true);
                } else {
                    rule.addOption("number", true);
                }
                continue;
            }

            // hibernate validator
            if ("org.hibernate.validator.constraints.NotEmpty".equals(annoType)) {
                rule.addOption("required", true);
                continue;
            }
            if ("org.hibernate.validator.constraints.Email".equals(annoType)) {
                rule.addOption("email", true);
                continue;
            }
            if ("org.hibernate.validator.constraints.URL".equals(annoType)) {
                rule.addOption("url", true);
                continue;
            }
            if ("org.hibernate.validator.constraints.Length".equals(annoType)) {
                Object o = anno.getNamedParameter("min");
                int min = null != o ? asInt(o) : 0;
                o = anno.getNamedParameter("max");
                int max = null != o ? asInt(o) : 255;

                rule.addOption("minlength", min);
                rule.addOption("maxlength", max);
                continue;
            }
            // System.out.println("validation: ignore annotation: " + annoType + " on " + e);
        }
        return rule;
    }

    public static class FormField {
        private final String name;
        private final String fullName;
        private final String code;
        private final String type;
        private final Rule rule;

        public FormField(String name, String fullName, String code, String type, Rule rule) {
            this.name = name;
            this.fullName = fullName;
            this.code = code;
            this.type = type;
            this.rule = rule;
        }

        public String getName() {
            return name;
        }

        public String getFullName() {
            return fullName;
        }

        public String getCode() {
            return code;
        }

        public String getType() {
            return type;
        }

        public Rule getRule() {
            return rule;
        }
    }

    public static class Rule extends HashMap<String, Object> {
        private final String name;

        public static Rule create(String name) {
            return new Rule(name);
        }

        Rule(String name) {
            this.name = name;
        }

        void addOption(String name, Object value) {
            put(name, value);
        }

        public String getName() {
            return name;
        }

        public Map<String, Object> getOptions() {
            return Collections.unmodifiableMap(this);
        }
    }
}
